﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security.Authentication;

namespace org.furia.proxy
{
    public partial class Server : Component
    {
        public Server()
        {
            InitializeComponent();
            clientDisconnectedDelegate = new ClientEvent(ClientDisconnected);
        }

        public Server(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
            clientDisconnectedDelegate = new ClientEvent(ClientDisconnected);
        }

        #region Properties
        private bool master = false;
        public bool Master
        {
            get
            {
                return master;
            }
            set
            {
                master = value;
            }
        }

        private int localPort = 0;
        public int LocalPort
        {
            get
            {
                return localPort;
            }
            set
            {
                localPort = value;
            }
        }

        private string remoteHost = string.Empty;
        public string RemoteHost
        {
            get
            {
                return remoteHost;
            }
            set
            {
                remoteHost = value;
            }
        }

        private int remotePort = 0;
        public int RemotePort
        {
            get
            {
                return remotePort;
            }
            set
            {
                remotePort = value;
            }
        }

        private X509Certificate certificate;
        public X509Certificate Certificate
        {
            get
            {
                return certificate;
            }
            set
            {
                certificate = value;
            }
        }

        private X509Certificate authority;
        public X509Certificate Authority
        {
            get
            {
                return authority;
            }
            set
            {
                authority = value;
            }
        }

        private int bufferSize = 0;
        public int BufferSize
        {
            get
            {
                return bufferSize;
            }
            set
            {
                bufferSize = value;
            }
        }
        #endregion

        private TcpListener listener;
        private List<Client> clients;

        public void Start()
        {
            clients = new List<Client>();
            try
            {
                listener = new TcpListener(IPAddress.Any, localPort);
                listener.Start();
                DoAccept(listener);
            }
            catch
            {
                Stop();
            }
        }

        private void DoAccept(TcpListener listener)
        {
            try
            {
                listener.BeginAcceptTcpClient(new AsyncCallback(OnAccept), null);
            }
            catch
            {
                Stop();
            }
        }

        private void OnAccept(IAsyncResult result)
        {
            try
            {
                TcpClient socket = listener.EndAcceptTcpClient(result);

                Client client = new Client();
                client.Authority = authority;
                client.Certificate = certificate;
                client.RemoteHost = remoteHost;
                client.RemotePort = remotePort;
                client.Master = master;
                client.BufferSize = bufferSize;
                clients.Add(client);
                client.Disconnected += clientDisconnectedDelegate;
                client.Start(socket);

                DoAccept(listener);
            }
            catch
            {
                Stop();
            }

        }

        private ClientEvent clientDisconnectedDelegate = null;
        void ClientDisconnected(object sender)
        {
            Client client = sender as Client;
            client.Disconnected -= clientDisconnectedDelegate;
            if (clients != null)
            {
                clients.Remove(client);
            }
        }

        public void Stop()
        {
            if (listener != null)
            {
                try
                {
                    listener.Stop();
                }
                catch { }
                listener = null;
            }
            while (clients != null && clients.Count > 0)
            {
                Client client = clients[0];
                client.Stop();
            }
            clients = null;
        }
    }
}
